import * as fs from 'fs';

/**
 * Lightweight regex-based parser for extracting Vuex action names
 * This approach eliminates the need for TypeScript compiler dependency
 * Based on project experience with regex-based JavaScript parsing
 */
export class RegexActionParser {
  
  /**
   * Extract action names from module files using regex patterns
   * Rewritten following 5-step process like getRootActionNames:
   * 1. Read file and set built-in actionsToUse, remove comments (unchanged logic)
   * 2. Extract content between 'actions: {' and '}' (: may have spaces)
   * 3. Remove spaces and line breaks
   * 4. Split by comma to get actionList
   * 5. Loop through actionList, if actionItem contains '(', split and take first part, skip if empty
   */
  static getModuleActionNames(filePath: string, modelName?: string, innerActions?: string[]): string[] {
    try {
      // Step 1: Read file and set built-in actionsToUse, remove comments
      let fileContent = fs.readFileSync(filePath, 'utf8');
      const actionNames: string[] = [];
      // Use provided innerActions or fallback to default list
      const defaultInnerActions = ['setObject', 'setData', 'setConfig', 'pushData', 'resetFormData', 'copyByItem', 'copyByMap', 'copyByList'];
      const actionsToUse = innerActions || defaultInnerActions;
      
      // Remove comments (existing logic unchanged)
      fileContent = this.removeComments(fileContent);
      
      // Step 2: Extract content between 'actions:' and '}' (: may have spaces)
      // First, find the actions block with proper brace matching
      const actionsStartPattern = /actions\s*:\s*\{/;
      const actionsStartMatch = fileContent.match(actionsStartPattern);
      
      if (!actionsStartMatch) {
        console.warn(`No actions block found in module ${filePath}`);
        return actionNames;
      }
      
      const startIndex = actionsStartMatch.index! + actionsStartMatch[0].length - 1; // Position of opening brace
      let braceCount = 1;
      let endIndex = startIndex + 1;
      
      // Find the matching closing brace
      while (endIndex < fileContent.length && braceCount > 0) {
        const char = fileContent[endIndex];
        if (char === '{') {
          braceCount++;
        } else if (char === '}') {
          braceCount--;
        }
        endIndex++;
      }
      
      if (braceCount !== 0) {
        console.warn(`Unmatched braces in actions block in module ${filePath}`);
        return actionNames;
      }
      
      let actionsContent = fileContent.substring(startIndex + 1, endIndex - 1);
      
      // Step 3: Remove spaces and line breaks, but keep some structure for better parsing
      // First, remove excessive whitespace but preserve line structure for better parsing
      actionsContent = actionsContent.replace(/\s+/g, ' ').replace(/\n/g, ' ');
      
      // Step 4: Split by comma to get actionList
      // First, let's clean up some obvious non-action patterns
      actionsContent = actionsContent.replace(/\/\/@ts-ignore/g, ''); // Remove TypeScript ignore comments
      
      const actionList = actionsContent.split(',').filter(item => item.trim() !== '');
      
      // Step 5: Loop through actionList
      for (let actionItem of actionList) {
        actionItem = actionItem.trim();
        
        // Skip empty items
        if (!actionItem || actionItem === '') {
          continue;
        }
        
        // Handle spread operations like ...baseActions
        if (actionItem.startsWith('...')) {
          const spreadItem = actionItem.substring(3); // Remove '...'
          if (spreadItem === 'baseActions') {
            // Add the built-in actions
            actionsToUse.forEach(action => {
              const actionName = modelName ? `${modelName}/${action}` : action;
              if (!actionNames.includes(actionName)) {
                actionNames.push(actionName);
              }
            });
          }
          continue;
        }
        
        // If actionItem contains '(', split and take first part
        if (actionItem.includes('(')) {
          actionItem = actionItem.split('(')[0];
        }
        
        // If actionItem contains ':', it might be a function assignment, take the part before ':'
        if (actionItem.includes(':')) {
          const parts = actionItem.split(':');
          actionItem = parts[0];
        }
        
        // Handle async function declarations
        if (actionItem.includes('async')) {
          actionItem = actionItem.replace(/^\s*async\s+/, '').trim();
        }
        
        // Clean up action name (remove colons, braces, and other artifacts)
        actionItem = actionItem.replace(/[:\{\}]/g, '').trim();
        
        // Additional cleanup - remove common code patterns
        actionItem = actionItem.replace(/\s*return\s+.*/, ''); // Remove return statements
        actionItem = actionItem.replace(/\s*\[.*\].*/, ''); // Remove array access patterns
        actionItem = actionItem.replace(/\s*\{.*\}.*/, ''); // Remove object patterns
        actionItem = actionItem.replace(/\s*msg\s*:.*/, ''); // Remove object property patterns
        actionItem = actionItem.replace(/\s*data\s*:.*/, ''); // Remove data property patterns
        actionItem = actionItem.trim();
        
        // Skip if empty after processing
        if (!actionItem || actionItem === '') {
          continue;
        }
        
        // Skip comments and TypeScript ignore patterns
        if (actionItem.startsWith('//') || actionItem.startsWith('/*') || actionItem.includes('@ts-ignore')) {
          continue;
        }
        
        // Skip common code patterns that are not actions
        const codePatterns = [
          /^code\d+$/, // Things like 'code0'
          /^msg$/, // 'msg'
          /^data$/, // 'data'
          /.*loading.*/, // Things containing 'loading'
          /^\d+$/, // Pure numbers
          /^[a-z]+\d+$/, // Lowercase + numbers pattern
          /territory/, // Territory related
          /params/, // Params related
          /sourceType/, // Source type related
        ];
        
        if (codePatterns.some(pattern => pattern.test(actionItem))) {
          continue;
        }
        
        // Skip baseActions (already handled above)
        if (actionItem === 'baseActions') {
          continue;
        }
        
        // Skip if doesn't match a valid JavaScript identifier pattern
        if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(actionItem)) {
          continue;
        }
        
        // Add to action names with model name prefix if provided
        const actionName = modelName ? `${modelName}/${actionItem}` : actionItem;
        if (!actionNames.includes(actionName)) {
          actionNames.push(actionName);
          console.log(`Extracted module action: ${actionName}`);
        }
      }
      
      return actionNames;
    } catch (error) {
      console.error('Error parsing module file:', error);
      return [];
    }
  }
  
  /**
   * Extract action names from root store files
   * New implementation following 5-step process:
   * 1. Read file and set built-in actionsToUse, remove comments (unchanged logic)
   * 2. Extract content between 'actions: {' and '}' (: may have spaces)
   * 3. Remove spaces and line breaks
   * 4. Split by comma to get actionList
   * 5. Loop through actionList, if actionItem contains '(', split and take first part, skip if empty
   */
  static getRootActionNames(filePath: string, innerActions?: string[]): string[] {
    try {
      // Step 1: Read file and set built-in actionsToUse, remove comments
      let fileContent = fs.readFileSync(filePath, 'utf8');
      const actionNames: string[] = [];
      // Use provided innerActions or fallback to default list
      const defaultInnerActions = ['setObject', 'setData', 'setConfig', 'pushData', 'resetFormData', 'copyByItem', 'copyByMap', 'copyByList'];
      const actionsToUse = innerActions || defaultInnerActions;
      
      // Remove comments (existing logic unchanged)
      fileContent = this.removeComments(fileContent);
      
      // Step 2: Extract content between 'actions:' and '}' (: may have spaces)
      // First, find the actions block with proper brace matching
      const actionsStartPattern = /actions\s*:\s*\{/;
      const actionsStartMatch = fileContent.match(actionsStartPattern);
      
      if (!actionsStartMatch) {
        console.warn(`No actions block found in ${filePath}`);
        return actionNames;
      }
      
      const startIndex = actionsStartMatch.index! + actionsStartMatch[0].length - 1; // Position of opening brace
      let braceCount = 1;
      let endIndex = startIndex + 1;
      
      // Find the matching closing brace 
      while (endIndex < fileContent.length && braceCount > 0) {
        const char = fileContent[endIndex];
        if (char === '{') {
          braceCount++;
        } else if (char === '}') {
          braceCount--;
        }
        endIndex++;
      }
      
      if (braceCount !== 0) {
        console.warn(`Unmatched braces in actions block in ${filePath}`);
        return actionNames;
      }
      
      let actionsContent = fileContent.substring(startIndex + 1, endIndex - 1);
      
      // Step 3: Remove spaces and line breaks, but keep some structure for better parsing
      // First, remove excessive whitespace but preserve line structure for better parsing
      actionsContent = actionsContent.replace(/\s+/g, ' ').replace(/\n/g, ' ');
      
      // Step 4: Split by comma to get actionList
      // First, let's clean up some obvious non-action patterns
      actionsContent = actionsContent.replace(/\/\/@ts-ignore/g, ''); // Remove TypeScript ignore comments
      console.log("🚀 ~ RegexActionParser ~ getRootActionNames ~ actionsContent:", actionsContent);
      
      const actionList = actionsContent.split(',').filter(item => item.trim() !== '');
      
      // Step 5: Loop through actionList
      for (let actionItem of actionList) {
        actionItem = actionItem.trim();
        
        // Skip empty items
        if (!actionItem || actionItem === '') {
          continue;
        }
        
        // Handle spread operations like ...baseActions
        if (actionItem.startsWith('...')) {
          const spreadItem = actionItem.substring(3); // Remove '...'
          if (spreadItem === 'baseActions') {
            // Add the built-in actions
            actionsToUse.forEach(action => {
              if (!actionNames.includes(action)) {
                actionNames.push(action);
              }
            });
          }
          continue;
        }
        
        // If actionItem contains '(', split and take first part
        if (actionItem.includes('(')) {
          actionItem = actionItem.split('(')[0];
        }
        
        // If actionItem contains ':', it might be a function assignment, take the part before ':'
        if (actionItem.includes(':')) {
          const parts = actionItem.split(':');
          actionItem = parts[0];
        }
        
        // Handle async function declarations
        if (actionItem.includes('async')) {
          actionItem = actionItem.replace(/^\s*async\s+/, '').trim();
        }
        
        // Clean up action name (remove colons, braces, and other artifacts)
        actionItem = actionItem.replace(/[:\{\}]/g, '').trim();
        
        // Additional cleanup - remove common code patterns
        actionItem = actionItem.replace(/\s*return\s+.*/, ''); // Remove return statements
        actionItem = actionItem.replace(/\s*\[.*\].*/, ''); // Remove array access patterns
        actionItem = actionItem.replace(/\s*\{.*\}.*/, ''); // Remove object patterns
        actionItem = actionItem.replace(/\s*msg\s*:.*/, ''); // Remove object property patterns
        actionItem = actionItem.replace(/\s*data\s*:.*/, ''); // Remove data property patterns
        actionItem = actionItem.trim();
        
        // Skip if empty after processing
        if (!actionItem || actionItem === '') {
          continue;
        }
        
        // Skip comments and TypeScript ignore patterns
        if (actionItem.startsWith('//') || actionItem.startsWith('/*') || actionItem.includes('@ts-ignore')) {
          continue;
        }
        
        // Skip common code patterns that are not actions
        const codePatterns = [
          /^code\d+$/, // Things like 'code0'
          /^msg$/, // 'msg'
          /^data$/, // 'data'
          /.*loading.*/, // Things containing 'loading'
          /^\d+$/, // Pure numbers
          /^[a-z]+\d+$/, // Lowercase + numbers pattern
          /territory/, // Territory related
          /params/, // Params related
          /sourceType/, // Source type related
        ];
        
        if (codePatterns.some(pattern => pattern.test(actionItem))) {
          continue;
        }
        
        // Skip baseActions (already handled above)
        if (actionItem === 'baseActions') {
          continue;
        }
        
        // Skip if doesn't match a valid JavaScript identifier pattern
        if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(actionItem)) {
          continue;
        }
        
        // Add to action names if not already present
        if (!actionNames.includes(actionItem)) {
          actionNames.push(actionItem);
          // console.log(`Extracted action: ${actionItem}`);
        }
      }
      
      return actionNames;
    } catch (error) {
      console.error('Error parsing root store file:', error);
      return [];
    }
  }
  
  /**
   * Extract action names from actions object content
   */
  private static extractActionNamesFromContent(
    content: string, 
    actionNames: string[], 
    modelName?: string, 
    innerActions: string[] = []
  ): void {
    // Handle spread operator for base actions
    if (content.includes('...baseActions')) {
      innerActions.forEach(action => {
        const actionName = modelName ? `${modelName}/${action}` : action;
        if (!actionNames.includes(actionName)) {
          actionNames.push(actionName);
        }
      });
    }
    
    // Match async method declarations: async methodName() { ... }
    const asyncMethodPattern = /async\s+(\w+)\s*\([^)]*\)\s*\{/g;
    let asyncMatch;
    while ((asyncMatch = asyncMethodPattern.exec(content)) !== null) {
      const actionName = asyncMatch[1];
      
      // Skip JavaScript keywords and baseActions
      if (actionName === 'function' || actionName === 'baseActions') {
        continue;
      }
      
      const fullActionName = modelName ? `${modelName}/${actionName}` : actionName;
      if (!actionNames.includes(fullActionName)) {
        actionNames.push(fullActionName);
      }
    }
    
    // Match regular method declarations: methodName() { ... }
    const methodPattern = /(\w+)\s*\([^)]*\)\s*\{/g;
    let methodMatch;
    while ((methodMatch = methodPattern.exec(content)) !== null) {
      const actionName = methodMatch[1];
      
      // Skip JavaScript keywords and baseActions
      if (actionName === 'function' || actionName === 'baseActions') {
        continue;
      }
      
      // Skip if it's already matched as async
      const beforeMethod = content.substring(Math.max(0, methodMatch.index! - 10), methodMatch.index!);
      if (!beforeMethod.includes('async')) {
        const fullActionName = modelName ? `${modelName}/${actionName}` : actionName;
        if (!actionNames.includes(fullActionName)) {
          actionNames.push(fullActionName);
        }
      }
    }
    
    // Match function expressions: methodName: function() { ... } or methodName: () => { ... }
    const functionPattern = /(\w+)\s*:\s*(?:function\s*\([^)]*\)|\([^)]*\)\s*=>)\s*\{/g;
    let functionMatch;
    while ((functionMatch = functionPattern.exec(content)) !== null) {
      const actionName = functionMatch[1];
      
      // Skip JavaScript keywords and baseActions
      if (actionName === 'function' || actionName === 'baseActions') {
        continue;
      }
      
      const fullActionName = modelName ? `${modelName}/${actionName}` : actionName;
      if (!actionNames.includes(fullActionName)) {
        actionNames.push(fullActionName);
      }
    }
    
    // Match shorthand property assignments: actionName, (but exclude baseActions)
    const shorthandPattern = /^\s*(\w+)\s*,?\s*$/gm;
    let shorthandMatch;
    while ((shorthandMatch = shorthandPattern.exec(content)) !== null) {
      const actionName = shorthandMatch[1];
      
      // Skip baseActions as it's not an actual action name
      if (actionName === 'baseActions') {
        continue;
      }
      
      const fullActionName = modelName ? `${modelName}/${actionName}` : actionName;
      if (!actionNames.includes(fullActionName)) {
        actionNames.push(fullActionName);
      }
    }
  }
  
  /**
   * Extract actions content with proper brace matching
   */
  private static extractActionsContent(content: string): string | null {
    const actionsMatch = content.match(/actions\s*:\s*\{/);
    if (!actionsMatch) {
      return null;
    }
    
    const startIndex = actionsMatch.index! + actionsMatch[0].length - 1; // Position of opening brace
    let braceCount = 1;
    let endIndex = startIndex + 1;
    
    // Find the matching closing brace
    while (endIndex < content.length && braceCount > 0) {
      const char = content[endIndex];
      if (char === '{') {
        braceCount++;
      } else if (char === '}') {
        braceCount--;
      }
      endIndex++;
    }
    
    if (braceCount === 0) {
      return content.substring(startIndex + 1, endIndex - 1);
    }
    
    return null;
  }
  
  /**
   * Remove comments from file content while preserving strings
   */
  private static removeComments(fileContent: string): string {
    // Remove multi-line comments
    fileContent = fileContent.replace(/\/\*[\s\S]*?\*\//g, '');
    
    // Process line by line for single-line comments
    const lines = fileContent.split('\n');
    const cleanedLines: string[] = [];
    
    for (let line of lines) {
      let inString = false;
      let stringChar = '';
      let cleanLine = '';
      
      for (let i = 0; i < line.length; i++) {
        const char = line[i];
        const nextChar = i < line.length - 1 ? line[i + 1] : '';
        const prevChar = i > 0 ? line[i - 1] : '';
        
        if (!inString && (char === '"' || char === "'")) {
          inString = true;
          stringChar = char;
          cleanLine += char;
        } else if (inString && char === stringChar && prevChar !== '\\') {
          inString = false;
          stringChar = '';
          cleanLine += char;
        } else if (!inString && char === '/' && nextChar === '/') {
          break;
        } else {
          cleanLine += char;
        }
      }
      
      cleanedLines.push(cleanLine);
    }
    
    return cleanedLines.join('\n');
  }
}